//***************************************************************************//
//*
//* 内容摘要：SDK对外主动接口类
//*	
//* 当前版本：V1.0		
//* 作    者：zhfayuan
//* 完成日期：2017-11-22
//**************************************************************************//
#if !defined(SDPLAYBACK_H)
#define SDPLAYBACK_H

#include "SDCommon.h"
#include "SDThread.h"
#include "SDLog.h"
#include "SDMutex.h"
#include "SDClientCommon.h"
#include "SDTermCommon.h"
#include "SDPlaybackIFace.h"
#include "SDPrivateMux.h"



//工作于集群模式还是非集群模式
#define WORK_IN_CLUSTER_MODE					0

//是否支持掉线自动重连
#define SUPPORT_AUTO_RECONNECT					0

class CSDPlayback;


//任务结构体
typedef struct _PlaybackTermTask
{
	int	        nPackType;		//包类型：正常任务包、结束包	
	UINT        unAvMsgIndex;
	UINT64      unSrcUid;
	UINT        unVkekToken;
	BYTE        byIndex;
} PlaybackTermTask;

typedef struct _PlaybackDecKeyPair
{
	UINT unAvMsgIndex;
	UINT64 unSrcUid;
	UINT   unVkekToken;
	BYTE   byIndex;
	BYTE   byVek[VEK_LEN];
}PlaybackDecKeyPair;

class CSDPlaybackAvCommon:public CAVProcess
{
    protected:
        UINT m_unAvMsgIndex;
        CSDPlayback* m_pTerm;
	private:
		PRIVATE_FILE m_PrivFile;
		FILE    *m_OutFile;
    protected:
		BOOL	m_bSeenSps;
	    UINT	m_unPacketsBeforeSps;
    protected:
        UINT	m_unJitterBuffDelay;
        FEC_REDUN_METHOD_TYPE m_eRedunMethod;
        UINT	m_unRedunRatio;
        UINT    m_unFecGroupSize;
		UINT    m_unVodPrevSec;
		UINT    m_unVodCurrSec;
    public:
        //父类虚函数的实现
        UINT64 GetAudioDownChannelsMask();
        UINT64 GetVideoDownChannelsMask();
    public:
        CSDPlaybackAvCommon(CSDPlayback*pCSDPlayback,UINT unAvMsgIndex,BYTE byUserType,BOOL bUseInternalTimestamp);
        virtual ~CSDPlaybackAvCommon();
    public:
		void SetPrivFile(PRIVATE_FILE privfile);
        void SetTransParams(UINT unJitterBuffDelay, FEC_REDUN_METHOD_TYPE eRedunMethod, UINT unRedunRatio, UINT unFecGroupSize, BOOL bEnableNack);
    public:
        //底层视频音频码流输出接口
        void OnRecvVideoStreamData(BYTE byCodec,BYTE byIndex, BYTE* data, UINT nLen, UINT unPTS, BOOL bComplete, BOOL bPrevTotalFrameLost,bool & bLostInGop);
        void OnRecvAudioStreamData(BYTE byCodec,BYTE byIndex, BYTE* data, UINT nLen, UINT unPts);
		void OnCheckedTokenUpdate(UINT64 unSrcUid, UINT unVkekToken, BYTE byIndex,BYTE byCrypto);
};

//SDK接口类
class CSDPlayback : public CSDMessage, public CSDSingleComSocket
{
public:

    DLLEXPORT CSDPlayback(CSDPlaybackIFace* pCltIFace,UINT LoginCode, BYTE byTermType = TERM_TYPE_PC_W, BOOL bUseInternalTimestamp = TRUE);
    DLLEXPORT virtual ~CSDPlayback();

public: 
	// 登录服务器
    DLLEXPORT int IFace_Online(const char* strIp,UINT nPort,BYTE byUserType,UINT64 unUid, BYTE byNetType = OPERATOR_OTHER);

	// 从服务器下线
    DLLEXPORT void IFace_Offline();

	// 请求上传AV到指定位置【Online成功后调用】
    DLLEXPORT int IFace_Play(std::string url,BYTE byUserType);

    DLLEXPORT int IFace_GetIndex(std::string url);

	// 请求从指定位置下来【Online成功后调用】
    DLLEXPORT void IFace_Stop(UINT unAvMsgIndex);

    DLLEXPORT void IFace_Seek(UINT unAvMsgIndex,UINT percent);

    DLLEXPORT void IFace_Pause(UINT unAvMsgIndex);

    DLLEXPORT void IFace_Resume(UINT unAvMsgIndex);



	//设置传输参数，若不调用本API将使用默认传输参数【必须Online之前调用】
    DLLEXPORT void IFace_SetTransParams(UINT unJitterBuffDelay, FEC_REDUN_METHOD_TYPE eRedunMethod, UINT unRedunRatio, UINT unFecGroupSize, BOOL bEnableNack);

	//获得音视频上下行丢包率
    DLLEXPORT void IFace_GetVideoAudioUpDownLostRatio(UINT unAvMsgIndex,float &fVideoUpLostRatio, float &fVideoDownLostRatio,
											float &fAudioUpLostRatio, float &fAudioDownLostRatio);
	//获得视频通道上当前的RTT
    DLLEXPORT UINT IFace_GetNetWorkDelayOnVideoChannel(UINT unAvMsgIndex);

	//获得音视频上下行码率
    DLLEXPORT void IFace_GetVideoAudioUpDownBitrate(UINT unAvMsgIndex,float &fVideoUpRate, float &fVideoDownRate, float &fAudioUpRate, float &fAudioDownRate);

	//获得当前P2P工作状态[本API对于非P2P版本无效，仅用于保持接口统一]
	E_P2P_Status IFace_GetP2PWorkStatus();

	//启用或者关闭P2P功能,默认不调用的话为启用[本API对于非P2P版本无效，仅用于保持接口统一]
	void IFace_EnableP2P(BOOL bEnableP2P);
	
	//设置本地视频发送帧率，从而启用本地发送Smooth处理功能注意该帧率要符合实际帧率,可以高于实际帧率，
	//但不能低于实际帧率，否则将导致发送速度不足。不调用本函数时，默认关闭smooth处理
	void IFace_SetVideoFrameRateForSmoother(UINT unFrameRate);

	//获得当前的版本号
    DLLEXPORT UINT IFace_GetVersion();

public:
	BOOL ForceSendLost(UINT unAvMsgIndex,LOST_METHOD_TYPE eLostMethod, UINT unLostValue, UINT unPacketsLostContinue = 1);
public:

	void OnVodPlayProgress(UINT unAvMsgIndex, UINT  sec, UINT duration);
	void OnCheckedTokenUpdate(UINT unAvMsgIndex, UINT64 unRoomId, UINT unVkekToken, BYTE byIndex,BYTE byCrypto);

	void OnRecvVideoStreamData(UINT unAvMsgIndex,BYTE byCodec,BYTE byIndex, BYTE* data, UINT nLen, UINT unPTS,VideoFrameInfo info);
	void OnRecvAudioStreamData(UINT unAvMsgIndex,BYTE byCodec,BYTE byIndex, BYTE* data, UINT nLen, UINT unPts,AudioFrameInfo info);
private:
	void TaskThread();
private:

	//底层TCP链接的一些错误处理
	void Error(int nErrorId);

	void Command(PACK_MESSAGE* pack);
	int  ProcessMessage(void* pParam);
	BOOL RespondPackMessage(PACK_MESSAGE *pack)
	{
		CSDSingleComSocket::UpdateSynRetBuf(pack);
		return TRUE;
	}

private:

	BOOL mfIsWork() { return CSDSingleComSocket::IsWork() && m_bOnlineStatus; };
	BOOL mfInit();
	void mfClose(BOOL bCloseHandThread, BOOL bCloseMessageThread = TRUE, BOOL bCloseTcpThread = TRUE, BOOL bCloseAvProcess = TRUE,BOOL bCloseAuthThread = TRUE);
	int  mfGetDecKey(UINT64 unSrcUid, UINT unAvMsgIndex, BYTE byIndex, UINT unVkekToken, BYTE byVkek[PK_LEN]);
	BOOL mfConnect(char* strIp, int nPortId, int nPingPort);
	int  mfProcessBroadcast(PACK_MESSAGE* pack);
	BOOL mfSendCmd1(USHORT shCmdMessage);
	BOOL mfSendCmd2(USHORT shCmdMessage,UINT unAvMsgIndex);
	BOOL mfSendCmd3(USHORT shCmdMessage,UINT64 other1,UINT64 other2);
	
	int  mfOnLine(UINT64 unUid,BYTE byUserType, BYTE byNetType);
	void mfOffLine();

	int  mfConnectMsgServer(UINT64 unUid,BYTE byNetType);


	void mfHandleMessage(PACK_MESSAGE *pack);
	static int IsTerminalCloseRequest(void* pParam);

private:
    void PrintHex(BYTE *pre, BYTE*by, UINT unLen);
    void MyAuthIdle(UINT &uSendInterval, UINT &unState);
    void MyPubKeyExchangeSerRandom(UINT &uSendInterval,UINT &unState,SM_RANDOM &stRandom);
    void MyRandomExchangeSerPubKey(UINT &uSendInterval,UINT &unState,SM_RANDOM &stRandom);
    void MyEmptyExchangeSerRandom(UINT &uSendInterval,UINT &unState,SM_RANDOM &stRandom);
    void MyCompositeRandomExchangeVkek(UINT &uSendInterval,UINT &unState,SM_RANDOM &stRandom);
private:	
	//TCP心跳线程
	static int HandleThread(void* pParam);
	static int HandleThreadClose(void* pParam);
    //加密认证线程
    static int AuthThread(void* pParam);
    static int AuthThreadClose(void* pParam);

	//
	static int TaskThreadProc(void* pParam);
	static int TaskThreadProcClose(void* pParam);
    static int SDEnum(struct stIdentity& identity);

private:
    map<std::string,UINT>    m_unAvUpPositionMap;
    map<UINT,CSDPlaybackAvCommon*> m_nRoomAvCommon;
private:
    CSDThread           m_AuthThread;
    CSDEvent            m_AuthRetryEvent;
private:
	CSDPlaybackIFace*	    m_pCltIFace;
	BOOL				m_bNoblockRequest;

    SM2_KEY_PAIR        m_stKeyPair;
    BYTE                m_byVek[VEK_LEN];

    BOOL                m_bUseInternalTimestamp;
    BOOL                m_bKeyNegotiated;

	char				m_strDomainSerIp[128];
    UINT                m_unSerPort;
    UINT                m_nConnectSerIp;

    UINT64              m_unUid;

    BYTE                m_unSeq;
	BYTE				m_byTermType;

	//0表示离线，1表示在线
	BOOL				m_bOnlineStatus;
	//资源是否回收
	BOOL				m_bCloseFlag;

	//是否已经通知外层底层连接错误，该标志用于避免多次通知的情况
	BOOL				m_bNotifyError;	
	//底层连接错误原因
	UINT				m_unErrorReason;	

	void*				m_execs;
	void*				m_cmdRecvCs;
    void*               m_avCs;


	//要发出的通用请求包,构造时分配内存
	PACK_MESSAGE*		m_packRequest;		

	//用于给当前服务器发送周期性心跳包的线程
	CSDThread			m_HandleThread;
	CSDEvent			m_HandleEvent;


	bool				m_bTcpRecvThreadIsCreate;

    //参数 For AVProcess
    UINT	m_unJitterBuffDelay;
    FEC_REDUN_METHOD_TYPE m_eRedunMethod;
    UINT	m_unRedunRatio;
    UINT    m_unFecGroupSize;
    BOOL    m_bEnableNack;

	//收到当前服务器心跳包的计数，用于判断该客户端是否已经处于无效状态
	UINT				m_unCurrHandPacketCount;

	//标识本次主动登录（不包括内部掉线重连，内部重连使用不变的唯一登录码）的唯一登录码
	UINT				m_unLogonCode;

public:
	BOOL		    m_bTaskThreadExit;
	// 线程任务队列，线程池中每个线程都有一个用于接受任务的队列
	CSDThread       m_aTaskThread;
	DualQueId       m_aTaskThreadTaskQue;
	bool	    	m_bTaskThreadIsCreate;
};
#endif // !defined(SDTERMINAL_H)
